/*
 * Copyright 2014, NICTA
 *
 * This software may be distributed and modified according to the terms of
 * the BSD 2-Clause license. Note that NO WARRANTY is provided.
 * See "LICENSE_BSD2.txt" for details.
 *
 * @TAG(NICTA_BSD)
 */

/****************************************************************************
 *
 *      $Id:  $
 *
 *      Description: Simple milestone 0 code.
 *      		     Libc will need sos_write & sos_read implemented.
 *
 *      Author:      Ben Leslie
 *
 ****************************************************************************/

#include <stdarg.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>

#include "ttyout.h"

#include <sel4/sel4.h>
#include <sel4/constants.h>

#define MIN(a,b) (a > b ? b : a)
#define DIVROUND(a,b) ((a + (b - 1)) / b)

/* XXX This should be in a SOS shared header somewhere */
#define SOS_SYSCALL_WRITE_CONSOLE 0

void ttyout_init(void) {
    /* Perform any initialisation you require here */
}

/* Attempt single write, without retrying */
size_t _sos_write(void *vData, size_t count) {
    seL4_Word *realdata = (seL4_Word*) vData;
    seL4_IPCBuffer *buf = seL4_GetIPCBuffer();
    seL4_Word len = 2 + DIVROUND(count, 4);
    seL4_Word real_len = MIN(seL4_MsgMaxLength, len);
    size_t real_count  = MIN(count, (seL4_MsgMaxLength - 2) * 4);

    /* Set up syscall parameters */
    buf->msg[0] = SOS_SYSCALL_WRITE_CONSOLE;
    buf->msg[1] = real_count;

    int i;
    for (i = 2; i < len; i++) {
      buf->msg[i] = realdata[i - 2];
    }

    /* Fire off syscall and wait for reply */
    seL4_MessageInfo_t tag = seL4_MessageInfo_new(0, 0, 0, real_len);
    seL4_Call(SYSCALL_ENDPOINT_SLOT, tag);

    return seL4_GetMR(0);
}

/* Attempt writes until everything is written */
size_t sos_write(void *vData, size_t count) {
    seL4_Word *data;
    seL4_Word woffset;
    size_t bleft;

    seL4_Word *alldata = vData;
    size_t written = 0;

    while (written < count) {
      /* Adjust 'data' pointer and number of remaining bytes */
      woffset = DIVROUND(written, 4);
      data = alldata + woffset;
      bleft = count - written;

      /* Attempt to write remainder */
      written += _sos_write(data, bleft);
    }
 
    return written;
}

size_t sos_read(void *vData, size_t count) {
    //implement this to use your syscall
    return 0;
}
